package com.tomz.xatomic.xa;

import com.tomz.xatomic.Constant;
import com.tomz.xatomic.DynamicDataSourceContextHolder;
import org.apache.ibatis.session.*;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 分布式事务支持
 * @author ZHUFEIFEI
 */
public class XaDynamicSqlSessionFactory extends DefaultSqlSessionFactory {

    private SqlSessionFactory sqlSessionFactory;

    private Map<String, SqlSessionFactory> factoryMap = new ConcurrentHashMap<>();

    public XaDynamicSqlSessionFactory() {
        super(null);
        this.sqlSessionFactory = (SqlSessionFactory) Proxy.newProxyInstance(
                XaDynamicSqlSessionFactory.class.getClassLoader(),
                new Class[]{SqlSessionFactory.class},
                (proxy, method, args) ->  method.invoke(XaDynamicSqlSessionFactory.this.determineSqlSessionFactory(), args)
        );
    }

    public void add(String name, SqlSessionFactory sqlSessionFactory) {
        this.factoryMap.put(getKey(name), sqlSessionFactory);
    }

    public Map<String, SqlSessionFactory> sqlSessionFactories() {
        return Collections.unmodifiableMap(factoryMap);
    }

    @Override
    public SqlSession openSession() {
        return sqlSessionFactory.openSession();
    }

    @Override
    public SqlSession openSession(boolean autoCommit) {
        return sqlSessionFactory.openSession(autoCommit);
    }

    @Override
    public SqlSession openSession(ExecutorType execType) {
        return sqlSessionFactory.openSession(execType);
    }

    @Override
    public SqlSession openSession(TransactionIsolationLevel level) {
        return sqlSessionFactory.openSession(level);
    }

    @Override
    public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
        return sqlSessionFactory.openSession(execType, level);
    }

    @Override
    public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
        return sqlSessionFactory.openSession(execType, autoCommit);
    }

    @Override
    public SqlSession openSession(Connection connection) {
        return sqlSessionFactory.openSession(connection);
    }

    @Override
    public SqlSession openSession(ExecutorType execType, Connection connection) {
        return sqlSessionFactory.openSession(execType, connection);
    }

    @Override
    public Configuration getConfiguration() {
        return sqlSessionFactory.getConfiguration();
    }

    private Object determineSqlSessionFactory() {
        return factoryMap.getOrDefault(getKey(DynamicDataSourceContextHolder.get()), this.defaultSqlSessionFactory());
    }

    private SqlSessionFactory defaultSqlSessionFactory() {
        return this.factoryMap.values().iterator().next();
    }

    private String getKey(String name) {
        if (Objects.isNull(name)) {
            return Constant.DATA_SOURCE_DEFAULT_KEY;
        }
        return name;
//        return String.format("%s%s", PREFIX, name);
    }

}
